【网络流+线段树】[CQBZOJ3065]生死游戏((A+B)^2 Problem)

题目

题目描述
有些邪恶富人们喜欢玩生死游戏。简单的说就是找一些穷人,让他们进行相互厮杀。富人们则在旁观看并下注。
今天的游戏跟以往有些不同。参与游戏的穷人排成了一个n*m的矩阵,你的任务是组织游戏并决定这些人的生死。
如果第i行,第j列的人幸存了下来,你将获得Wij块钱,否则你将得到Bij块钱。
同时,富人们会提出一些奇怪的要求。他们每个人都会指定一个子矩阵,然后说:如果这个子矩阵的所有穷人都死了(或者都幸存下来了),你将得到S块钱。
你并不关心这些穷人的生死,你只希望你得到的钱尽可能多。
请注意,这里对杀人没有限制,你可以杀掉所有人,也可以一个也不杀。
输入
第一行,三个空格间隔的整数n,m,r ,分别表示矩阵的行数、列数和富人们提出的要求的数量。
接下来是n*m个整数,表示矩阵B。
接下来是n*m个整数,表示矩阵W。(0<=Bij,Wij<=100)
接下来r行,每行描述一个要求:
每个要求有6个空格间隔的整数构成:R1,C1,R2,C2,T,S。它表示一个子矩阵的左上角(R1,C1)和右下角(R2,C2)的坐标。T=1表示矩阵中的所有人必须死,T=0表示矩阵中所有人都必须活下来。S表示如果你满足了这个要求,你将得到的钱数。(0<=S<=10000)请注意,这里对杀人没有限制,你可以杀掉所有人,也可以一个也不杀。
输出
一行,一个整数,表示所求答案。
样例1
输入
2 2 3
34 44
63 30
1 9
53 57
1 2 2 2 1 2843
1 1 2 1 0 2169
2 1 2 1 1 6980

输出
9994

样例2
输入
2 2 3
50 93
65 70
52 28
91 25
1 1 2 1 0 9862
2 1 2 1 1 1876
2 2 2 2 0 4190

输出
14313

提示
对于30%的数据 1<=n,m<=10 0<=r=1000

对于50%的数据 1<=n,m<=30 0<=r<=10000

对于100%的数据 1<=n,m<=50 0<=r<=50000

分析

这道题的选择之间有关系,很容易想到最小割。

ans=i=1nj=1mbij+wiji,ji,jwi,ji,ji,jbi,jiisi

我们从S到每个点连容量为 bi 的边,从每个点到T连容量为 wi 的边,当点在S时,就代表这个人死了,在T则活着。
然后考虑富人的要求,对于每个要求我们新增一个点。
对于第 i 个要求:

  • 如果要求所有人都活着,那么当这个点在S时,就会付出si的代价。就从这个点到T连一条容量为 si 的边,再从矩阵内所有的点向这个点连一条容量为 + 的边。

    • 如果要求所有人都死,那么当这个点在T时,就会付出 si 的代价。就从S到这个点连一条容量为 si 的边,再从这个点到矩阵内所有的点连一条容量为 + 的边。

      显然边十分多,但是连接的点往往是在一个二维区间内的所有点,可以效仿A+B Problem,用二维线段树优化这道题。

    • 这道题,其实就是 (A+B)2Problem

      代码

      #include<cstdio>
      #include<cstring>
      #include<queue>
      #include<algorithm>
      #define MAXN 50
      #define MAXR 50000
      #define INF 0x7fffffff
      using namespace std;
      queue<int>q;
      struct node{
          int v,cap;
          node *back,*next;
      }*adj[MAXN*MAXN*20+MAXR+10],edge[10000000+10],*ecnt=edge;
      int root[MAXN*4+10],ls[MAXN*MAXN*4*4+10],rs[MAXN*MAXN*4*4+10],tot,m,S,T,n,r,ans,c,dist[MAXN*MAXN*20+MAXR+10],vd[MAXN*MAXN*20+MAXR+10],flow,id[MAXN*MAXN*4*4+10][2],tcnt;
      void addedge(int u,int v,int cap){
          node *p=++ecnt;
          p->v=v;
          p->cap=cap;
          p->next=adj[u];
          adj[u]=p;
          p=p->back=++ecnt;
          p->v=u;
          p->cap=0;
          p->next=adj[v];
          adj[v]=p;
          p->back=ecnt-1;
      }
      void build2(int &i,int l,int r,int ll,int rr){
          i=++tcnt;
          id[i][0]=++tot,id[i][1]=++tot;
          if(l==r){
              for(int j=ll;j<=rr;j++){
                  addedge(id[i][1],(j-1)*m+l,INF);
                  addedge((j-1)*m+l,id[i][0],INF);
              }
              return;
          }
          int mid=(l+r)>>1;
          build2(ls[i],l,mid,ll,rr);
          build2(rs[i],mid+1,r,ll,rr);
          addedge(id[i][1],id[ls[i]][1],INF);
          addedge(id[i][1],id[rs[i]][1],INF);
          addedge(id[ls[i]][0],id[i][0],INF);
          addedge(id[rs[i]][0],id[i][0],INF);
      }
      void build1(int i,int l,int r){
          build2(root[i],1,m,l,r);
          if(l==r)
              return;
          int mid=(l+r)>>1;
          build1(i<<1,l,mid);
          build1((i<<1)|1,mid+1,r);
      }
      void link2(int i,int l,int r,int ll,int rr,bool f,int c){
          if(ll<=l&&rr>=r){
              if(f)
                  addedge(tot,id[i][1],c);
              else 
                  addedge(id[i][0],tot,c);
              return;
          }
          if(l>rr||r<ll)
              return;
          int mid=(l+r)>>1;
          link2(ls[i],l,mid,ll,rr,f,c);
          link2(rs[i],mid+1,r,ll,rr,f,c);
      }
      void link1(int i,int l,int r,int ll,int rr,int wl,int wr,bool f,int c){
          if(ll<=l&&rr>=r){
              link2(root[i],1,m,wl,wr,f,c);
              return;
          }
          if(l>rr||r<ll)
              return;
          int mid=(l+r)>>1;
          link1(i<<1,l,mid,ll,rr,wl,wr,f,c);
          link1((i<<1)|1,mid+1,r,ll,rr,wl,wr,f,c);
      }
      void Read(int &x){
          char c;
          while(c=getchar(),c!=EOF)
              if(c>='0'&&c<='9'){
                  x=c-'0';
                  while(c=getchar(),c>='0'&&c<='9')
                      x=x*10+c-'0';
                  ungetc(c,stdin);
                  return;
              }
      }
      void read(){
          Read(n),Read(m),Read(r);
          int i,j,b,w;
          S=n*m+1,tot=T=S+1;
          build1(1,1,n);
          for(i=1;i<=n;i++)
              for(j=1;j<=m;j++){
                  Read(b);
                  addedge(S,(i-1)*m+j,b);
                  ans+=b;
              }
          for(i=1;i<=n;i++)
              for(j=1;j<=m;j++){
                  Read(w);
                  addedge((i-1)*m+j,T,w);
                  ans+=w;
              }
          int h1,h2,s1,s2,t,s;
          for(i=1;i<=r;i++){
              Read(h1),Read(s1),Read(h2),Read(s2),Read(t),Read(s);
              ans+=s;
              if(t){
                  addedge(S,++tot,s);
                  link1(1,1,n,h1,h2,s1,s2,1,s);
              }
              else{
                  addedge(++tot,T,s);
                  link1(1,1,n,h1,h2,s1,s2,0,s);
              }
      
          }
      }
      void bfs(){
          q.push(T);
          int u;
          while(!q.empty()){
              u=q.front();
              q.pop();
              for(node *p=adj[u];p;p=p->next){
                  if(p->back->cap&&!dist[p->v]){
                      dist[p->v]=dist[u]+1;
                      q.push(p->v);
                  }
              }
          }
          dist[T]=0;
      }
      int dfs(int u,int augu){
          if(u==T)
              return augu;
          int augv=0,v,delta,mind=tot-1;
          for(node *p=adj[u];p;p=p->next)
              if(p->cap){
                  v=p->v;
                  if(dist[u]==dist[v]+1){
                      delta=min(augu-augv,p->cap);
                      delta=dfs(v,delta);
                      augv+=delta;
                      p->cap-=delta;
                      p->back->cap+=delta;
                      if(augu==augv||dist[S]>=tot)
                          return augv;
                  }
                  mind=min(dist[v],mind);
              }
          if(!augv){
              if(!--vd[dist[u]])
                  dist[S]=tot;
              vd[dist[u]=mind+1]++;
          }
          return augv;
      }
      void sap(){
          bfs();
          for(int i=1;i<=tot;i++){
              if(!dist[i]){
                  dist[i]=tot;
                  continue;
              }
              vd[dist[i]]++;
          }
          dist[T]=0,vd[0]++;
          while(dist[S]<tot)
              flow+=dfs(S,INF);
      }
      int main()
      {
          read();
          sap();
          printf("%d\n",ans-flow);
      }
      
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值